#include "uti_instdb.h"


#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/format.hpp>
using namespace std;
using namespace boost::filesystem;




CInstDB::CInstDB(char* tradingday)
{
	strcpy(m_tradingday, tradingday);

	//first write then read
	//read would use write data for init
	if (false == InitDBInfo_w())
	{
		pantheios::log_ERROR("[CInstDB] InitDBInfo_w failed");
		return;
	}

	if (false == CheckDBFile())
	{
		pantheios::log_ERROR("[CInstDB] CheckDBFile failed");
		return;
	}

	if (false == InitBuffer())
	{
		pantheios::log_ERROR("[CInstDB] InitBuffer failed");
		return;
	}




	if (false == InitDBInfo_r())
	{
		pantheios::log_ERROR("[CInstDB] InitDBInfo_r failed");
		return;
	}


	
}


CInstDB::~CInstDB()
{
	
	//CloseDB();

	DestroyBuffer();

	DestroyDBInfo_w();

	DestroyDBInfo_r();

};

bool CInstDB::CheckDBFile()
{

	/*********************Cretate Parent and sub folder********************************/
	string& dbfolder = m_pdbinfo_w->dbfolder;
	
	try
	{
		path dbf(dbfolder);
		
		//check parent folder exist?
		if (!exists(dbf) || !is_directory(dbf))
		{

			if (create_directories(dbf))
				pantheios::log_NOTICE("[CheckDBFile] Create dbf:", dbf.string().c_str(), " successed\n");
			else
			{
				pantheios::log_ERROR("[CheckDBFile] Create dbf:", dbf.string().c_str(), " failed\n");
				return false;
			}	
		}


	}
	catch (const filesystem_error& ex)
	{
		cout << ex.what() << '\n';
		return false;
	}


	/***************************************************************************************************/


	/********************check dbfile exist? yes then delete it.****Init m_vdbname**********************/

	string dbfile = m_pdbinfo_w->dbname;
	
	try
	{
		path dbf(dbfile);

		if (exists(dbf) && !is_directory(dbf))
		{
			if (remove(dbf) == false)
			{
				pantheios::log_ERROR("[CheckDBFile] remove:", dbf.filename().string(), " failed\n");
				return false;
			}
		}


	}
	catch (const filesystem_error& ex)
	{
		cout << ex.what() << '\n';
		return false;
	}

	/***************************************************************************************************/

	
	return true;
}



bool CInstDB::InitDBInfo_w()
{
	//m_pdbinfo_w = NULL;
	const string inst = "inst";

	string dbname;
	string tbname;
	string dbfolder;

	

	string prefix = FOLDER_PREFIX;
	if (0 ==RUN_STATE || 1 ==RUN_STATE)
	{
		dbfolder = prefix + "/" + inst + "/test";
	} 
	else
	{
		dbfolder = prefix + "/" + inst;
	}


	dbname = dbfolder + "/" + inst + "_" + m_tradingday;
	tbname = inst + "table";


	m_pdbinfo_w = new DBINFO(dbname, tbname, dbfolder);
	if (NULL == m_pdbinfo_w)
	{
		pantheios::log_ERROR("[InitDBInfo_w] pdbinfo init failed");
		return false;
	}

	return true;
}

void CInstDB::DestroyDBInfo_w()
{
	if (m_pdbinfo_w)
	{
		delete m_pdbinfo_w;
		m_pdbinfo_w = NULL;
	}
}

bool CInstDB::InitDBInfo_r()
{
	/****************1.get db file and sort****************************/
	//read and write have the same folder
	path sf(m_pdbinfo_w->dbfolder);
	vector<path> v;             // store paths,  // so we can sort them later
	copy(directory_iterator(sf), directory_iterator(), back_inserter(v));
	sort(v.begin(), v.end()); //sort 

	/************************************************************************/


	/****************2.init DBInfo ***********************************/

	//filter all file

	string filename = "";

	for (vector <path>::const_iterator rdf_it = v.begin(); rdf_it != v.end(); rdf_it++)
	{
		string loc_filename = rdf_it->filename().string();	
		string::size_type pos = loc_filename.find(m_tradingday);

		if (pos == loc_filename.npos && 0 != strcmp("test", loc_filename.c_str()))
		{
			if (loc_filename > filename)
			{
				filename = loc_filename;
			}

		}
	}



	{
		//set with the latest file
		string dbname = m_pdbinfo_w->dbfolder + "/" + filename;	//dbname including prefix and dbname

		m_pdbinfo_r = new DBINFO(dbname, m_pdbinfo_w->tbname, m_pdbinfo_w->dbfolder);
		if (NULL == m_pdbinfo_r)
		{
			pantheios::log_ERROR("[InitDBInfo_r] m_pdbinfo_r init failed");
			return false;
		}

	}


	if (filename == "")
	{
		pantheios::log_ERROR("[InitDBInfo_r] filename NULL");
		return false;
	}


	/************************************************************************/

	return true;
}

void CInstDB::DestroyDBInfo_r()
{
	if (m_pdbinfo_r)
	{
		delete m_pdbinfo_r;
		m_pdbinfo_r = NULL;
	}
}


bool CInstDB::InitBuffer()
{
	m_buffer = new char[1024];//1K
	if (NULL == m_buffer)
	{
		pantheios::log_ERROR("[InitBuffer] m_buffer NULL");
		return false;
	}
	return true;

}

void CInstDB::DestroyBuffer()
{
	if (m_buffer)
	{
		delete[] m_buffer;
	}
}

bool CInstDB::WriteData(map<string, vector<InstInfo> >& instWrite)
{
		
	//1. Open DB
	if (false == OpenDB_w())
	{
		pantheios::log_ERROR("[WriteData] OpenDB_w failed");
		return false;
	}


	//2. WriteDB
	if (false == WriteDB_w(instWrite)) 
	{
		pantheios::log_ERROR("[WriteData] WriteDB_w failed");
		return false;
	}

	//3. Close DB
	if (false == CloseDB_w())
	{
		pantheios::log_ERROR("[WriteData] CloseDB_w failed");
		return false;
	}
	

	return true;
}


bool CInstDB::OpenDB_w()
{

	try
	{
		SQLiteDatabase* &curpdb = m_pdbinfo_w->pdb;
		SQLiteStatement* &curpst = m_pdbinfo_w->pst;
		string& curdbname = m_pdbinfo_w->dbname;
		string& curtbname = m_pdbinfo_w->tbname;

		/**********************Open DB********************************************/

		curpdb = new Kompex::SQLiteDatabase(curdbname, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, 0);
		// create statement instance for sql queries/statements
		curpst = new Kompex::SQLiteStatement(curpdb);

		//cout << "SQLite version: " << curpdb->GetLibVersionNumber() << std::endl;

		/************************************************************************/


		/**********************Create Table*****************************************/
		memset(m_buffer, 0, sizeof(m_buffer));
		sprintf(m_buffer, "select name from sqlite_master where type='table' and name = '%s';", curtbname.c_str());

		const char *tbName = (const char*)curpst->GetSqlResultCString(m_buffer);

		//curpst->FreeQuery();

		if (NULL == tbName)//table note exist, create it
		{
			memset(m_buffer, 0, sizeof(m_buffer));
				

			sprintf(m_buffer, "CREATE TABLE %s (%s, %s, %s, %s, %s, %s);", 
			curtbname.c_str(), "id INTEGER NOT NULL PRIMARY KEY", "product VARCHAR(15)", "inst VARCHAR(15)",
			"pricetick DOUBLE", "vol DOUBLE", "preOpenInterest DOUBLE");


			//printf(m_buffer);
			//int len = strlen(m_buffer);

			curpst->SqlStatement(m_buffer);


			//curpst->SqlStatement("PRAGMA synchronous=OFF;");
		}
		else//exist talbe for second write
		{
			if (tbName)
				delete tbName;

			pantheios::log_ERROR("[OpenDB_w] exist table:", curtbname.c_str());
		}

		

		/************************************************************************/

	}
	catch(Kompex::SQLiteException &exception)
	{
		std::cerr << "[OpenDB_w] Exception Occured" << std::endl;
		exception.Show();
		return false;
	}
	
	return true;
}

bool CInstDB::CloseDB_w()
{

	try
	{
		SQLiteDatabase* &curpdb = m_pdbinfo_w->pdb;
		SQLiteStatement* &curpst = m_pdbinfo_w->pst;
		string& curdbname = m_pdbinfo_w->dbname;
		string& curtbname = m_pdbinfo_w->tbname;

		/**********************Close DB********************************************/

		if (curpdb)
		{
			delete curpdb;
		}

		if (curpst)
		{
			delete curpst;
		}

		/************************************************************************/


	}
	catch(Kompex::SQLiteException &exception)
	{
		std::cerr << "[CloseDB_w] Exception Occured" << std::endl;
		exception.Show();
		return false;
	}



	return true;
}

bool CInstDB::WriteDB_w(map<string, vector<InstInfo> >& instWrite)
{

	SQLiteStatement* &curpst = m_pdbinfo_w->pst;
	string& dbname = m_pdbinfo_w->dbname;
	string& tbName =  m_pdbinfo_w->tbname;

		

	try
	{
		curpst->BeginTransaction();

		int id = 0;


		typedef map<string, vector<InstInfo> >::iterator it_mappdt;
		for(it_mappdt itp = instWrite.begin();itp != instWrite.end(); ++itp)
		{
			vector<InstInfo>& vInst = itp->second;

			typedef vector<InstInfo>::iterator it_vInst;
			for (it_vInst vit = vInst.begin(); vit != vInst.end(); vit++)
			{
				memset(m_buffer, 0, sizeof(m_buffer));
				sprintf(m_buffer, "INSERT INTO %s (%s, %s, %s, %s, %s, %s)\
								  VALUES (%d, '%s', '%s', %0.2f, %0.2f, %0.2f);", 
								  tbName.c_str(), "id", "product", "inst", "pricetick", "vol", "preOpenInterest",
								  id++, (itp->first).c_str(), vit->inst, vit->pricetick, vit->volumn, vit->preOpenInterest);

		/*		int len = strlen(m_buffer);

				printf(m_buffer);	 */

				curpst->SqlStatement(m_buffer);
			}

		}



		curpst->CommitTransaction();



	}
	catch(Kompex::SQLiteException &exception) 
	{
		std::cerr <<"[WriteDB_w] "<< dbname.c_str()<<" Exception Occured: " << exception.GetString();
		m_pdbinfo_w->pst->RollbackTransaction();
		std::cerr << "[WriteDB_w] Rollback was executed!" << std::endl;


		return false;
	}


	return true;

}



//open DB
//read DB
//close DB
bool CInstDB::ReadData(map<string, vector<InstInfo> >& instRead)
{


	
	//1. Open DB
	if (false == OpenDB_r())
	{
		pantheios::log_ERROR("[ReadData] OpenDB_r failed");
		return false;
	}


	//2. Read DB
	if (false == ReadDB_r(instRead)) 
	{
		pantheios::log_ERROR("[ReadData] ReadDB_r failed");
		return false;
	}

	//3. Close DB
	if (false == CloseDB_r())
	{
		pantheios::log_ERROR("[ReadData] CloseDB_r failed");
		return false;
	}




	return true;
}



bool CInstDB::OpenDB_r()
{

	try
	{
		SQLiteDatabase* &curpdb = m_pdbinfo_r->pdb;
		SQLiteStatement* &curpst = m_pdbinfo_r->pst;
		string& curdbname = m_pdbinfo_r->dbname;
		string& curtbname = m_pdbinfo_r->tbname;

		/**********************Open DB********************************************/

		curpdb = new Kompex::SQLiteDatabase(curdbname, SQLITE_OPEN_READONLY, 0);
		// create statement instance for sql queries/statements
		curpst = new Kompex::SQLiteStatement(curpdb);

		/*************************************************************************/


		//test select table
		/**********************Check TableName********************************************/
		memset(m_buffer, 0, sizeof(m_buffer));
		sprintf(m_buffer, "SELECT name FROM sqlite_master WHERE type='table' ORDER BY name;", curtbname.c_str());

		curpst->Sql(m_buffer);

		while(curpst->FetchRow())
		{
			//std::cout << "[OpenDB_r] dbname: "<<curdbname.c_str()<<" tableName: " << curpst->GetColumnString("name") << std::endl;

			pantheios::log_NOTICE("[OpenDB_r] dbname:", curtbname.c_str(), " tableName: ", curpst->GetColumnString("name"));
		}

		curpst->FreeQuery();
		/*********************************************************************************/

		//std::cout << std::endl;
		//memset(m_buffer, 0, sizeof(m_buffer));
		//sprintf(m_buffer, "SELECT * FROM %s;", curtbname);
		//curpst->GetTable(m_buffer, 8);
		//std::cout << std::endl;


		/**********************Check Record count********************************************/
		memset(m_buffer, 0, sizeof(m_buffer));
		sprintf(m_buffer, "SELECT COUNT(*) FROM %s;", curtbname.c_str());
		//std::cout << "[OpenDB_r] "<<curdbname.c_str()<<" COUNT(*): " << curpst->SqlAggregateFuncResult(m_buffer) << std::endl;

		pantheios::log_NOTICE("[OpenDB_r] ", curtbname.c_str(), " COUNT(*):  ", panreal(curpst->SqlAggregateFuncResult(m_buffer)));


		curpst->FreeQuery();
		/*********************************************************************************/



	}
	catch(Kompex::SQLiteException &exception)
	{
		std::cerr << "[OpenDB_r] Exception Occured" << std::endl;
		exception.Show();
		return false;
	}


	return true;
}

bool CInstDB::CloseDB_r()
{
	
	try
	{
		SQLiteDatabase* &curpdb = m_pdbinfo_r->pdb;
		SQLiteStatement* &curpst = m_pdbinfo_r->pst;
		string& curdbname = m_pdbinfo_r->dbname;
		string& curtbname = m_pdbinfo_r->tbname;

		/**********************Close DB********************************************/

		if (curpdb)
		{
			delete curpdb;
		}

		if (curpst)
		{
			delete curpst;
		}

		/************************************************************************/


	}
	catch(Kompex::SQLiteException &exception)
	{
		std::cerr << "[CloseDB_r] Exception Occured" << std::endl;
		exception.Show();
		return false;
	}


	return true;
}


bool CInstDB::ReadDB_r(map<string, vector<InstInfo> >& instRead)
{
	try
	{
		SQLiteDatabase* &curpdb = m_pdbinfo_r->pdb;
		SQLiteStatement* &curpst = m_pdbinfo_r->pst;
		string& curdbname = m_pdbinfo_r->dbname;
		string& curtbname = m_pdbinfo_r->tbname;
		


		memset(m_buffer, 0, sizeof(m_buffer));
		sprintf(m_buffer, "SELECT * FROM %s;", curtbname.c_str());	
		curpst->Sql(m_buffer);


		// process all results
		while(curpst->FetchRow())
		{

			int id = curpst->GetColumnInt("id");
			const char* product = (const char*)curpst->GetColumnCString("product");
			const char* inst = (const char*)curpst->GetColumnCString("inst");
			double pricetick = curpst->GetColumnDouble("pricetick");
			double vol = curpst->GetColumnDouble("vol");
			double preOpenInterest = curpst->GetColumnDouble("preOpenInterest"); 
			

			typedef map<string, vector<InstInfo> >::iterator it_mappdt;
			
			it_mappdt it = instRead.find(product);
			if (it == instRead.end())
			{
				vector<InstInfo> vInst;

				pair<it_mappdt , bool> ret = instRead.insert(make_pair(string(product), vInst));
				if (false == ret.second)
				{
					pantheios::log_NOTICE("[Init] instRead product (", product, ") insert failed");
					return false;
				}

				it = instRead.find(product);		
			}


			


			InstInfo tmp((char*)inst, pricetick, vol, preOpenInterest);

			
			vector<InstInfo>& vInst = it->second;
			vInst.push_back(tmp);



		}

		// do not forget to clean-up
		curpst->FreeQuery();


	}
	catch(Kompex::SQLiteException &exception)
	{
		std::cerr <<"[ReadDB_r]"<< "Exception Occured" << std::endl;
		exception.Show();
		return false;
	}

	return true;
}